####################                  LISCENSE                #####################








###################################################################################



from tkinter import *
from tkinter import messagebox as box
from tkinter import filedialog\

import io
import pickle
from sys import exit
import math

############                   File IO, Menu Functions:               #############
###################################################################################
def Exit():
    if box.askokcancel("Quit", "You want to quit now? *sniff*"):
        box.showwarning("Quitter", "EXITING NOW")
        root.destroy()

def New():
    global file_data
    global frame
    global scale
    #reset the zoom tool to 1, so coordinates are correct
    scale = 1
    frame.delete(ALL)
    file_data = []
    pre_render()

def Open():
    filename = filedialog.askopenfilename()
    in_file = open(filename, "rb")
    global file_data
    global frame
    global scale
    scale = 1
    file_data = pickle.Unpickler(in_file).load()
    in_file.close()
    frame.delete(ALL)
    pre_render()
    
def Save():
    global file_data
    filename = filedialog.asksaveasfilename()
    out_file = open(filename, "wb")
    pickle.dump(file_data, out_file)
    out_file.close()

def Export():
    global frame
    myFormats = [('Postscript','*.ps'),]
    filename = filedialog.asksaveasfilename(filetypes=myFormats ,title="Export as postscript")
    frame.postscript(file=filename,colormode='color')

    
def About():
    box.showwarning("About")

def Contents():
    box.showwarning("Contents Activated")
###################################################################################
###################################################################################






############                       G-Code Generation                  #############
###################################################################################
def generate_g_code():
    global file_data
    global shape
    #generate additional paths needed for brushes, store in processed_data
    #Additionally, optimize cutting order for shortest path length
    processed_data = file_data
    tempg = []
    templ = ""
    box.showwarning("Testing","Generating G-Code")
    for shape in processed_data:
        if shape[0]==4:
            #raise the z axis
            templ = "G0 Z3.0"
            tempg.append(templ)
            #then rapid to the start point
            templ = "G0 X" + str(shape[1]) + " Y" + str(shape[2])
            tempg.append(templ)
            #then cut down
            templ = "G1 Z0.0"
            tempg.append(templ)
            #then cut across
            templ = "G1 X" + str(shape[3]) + " Y" + str(shape[4])
            tempg.append(templ)
        elif (shape[0]==3 or shape[0]== 2):
            templ = "GO Z3.0"
            tempg.append(templ)
            templ = "GO X" + str(shape[1]) + " Y" + str(shape[2])
            tempg.append(templ)
            templ = "G1 Z0.0"
            tempg.append(templ)
            templ = "G1 Y" + str(shape[4])
            tempg.append(templ)
            templ = "G1 X" + str(shape[3])
            tempg.append(templ)
            templ = "G1 Y" + str(shape[2])
            tempg.append(templ)
            templ = "G1 X" + str(shape[1])
            tempg.append(templ)
        elif (shape[0]==1):
            templ
            
    templ = "G0 Z3.0"
    tempg.append(templ)
    #put the z axis back up, since we are done cutting
    #and save it all
    filename = filedialog.asksaveasfilename()
    out_file = open(filename, "w")
    for item in tempg:
        out_file.write(item + "\n")
    out_file.close()
###################################################################################
###################################################################################






    

############                        Mouse Handling,ctrl:              #############
###################################################################################
    
def left_click_down(event):
    global t_corr_x
    global t_corr_y
    global m_corr_x
    global m_corr_y
    global scale
    global dragtoolvar
    global current_tool
    t_corr_x = event.x + p_corr_mod_x
    t_corr_y = event.y + p_corr_mod_y
    if current_tool == 101:
        dragtoolvar = frame.create_rectangle(t_corr_x,t_corr_y,t_corr_x,t_corr_y,outline="red", fill="", dash = (5,5))
    elif current_tool == 3:
        dragtoolvar = frame.create_line(t_corr_x,t_corr_y,t_corr_x,t_corr_y,fill="blue")
    elif current_tool == 4:
        dragtoolvar = frame.create_rectangle(t_corr_x,t_corr_y,t_corr_x,t_corr_y,outline="blue", fill="")
    elif current_tool == 2:
        dragtoolvar = frame.create_rectangle(t_corr_x,t_corr_y,t_corr_x,t_corr_y,outline="blue", fill="")
    elif current_tool == 1:
        dragtoolvar = frame.create_oval(t_corr_x,t_corr_y,t_corr_x,t_corr_y,fill="", outline = "blue")
        
def left_click_up(event):
    global t_corr_x
    global t_corr_y
    global m_corr_x
    global m_corr_y
    global p_corr_mod_x
    global p_corr_mod_y
    global dragtoolvar
    global scale
    new_x = event.x + p_corr_mod_x
    new_y = event.y + p_corr_mod_y
    draw_shape(t_corr_x,t_corr_y,new_x, new_y)
    frame.delete(dragtoolvar)
    

def grab(event):
    global m_corr_x
    global m_corr_y
    m_corr_y = event.y
    m_corr_x = event.x

def drag(event):
    global m_corr_x
    global m_corr_y
    global p_corr_mod_x
    global p_corr_mod_y
    if (m_corr_y-event.y < 0): frame.yview("scroll",m_corr_y-event.y,"units")
    elif (m_corr_y-event.y > 0): frame.yview("scroll",m_corr_y-event.y,"units")
    if (m_corr_x-event.x < 0): frame.xview("scroll",m_corr_x-event.x,"units")
    elif (m_corr_x-event.x > 0): frame.xview("scroll",m_corr_x-event.x,"units")
    p_corr_mod_x = p_corr_mod_x + m_corr_x-event.x
    p_corr_mod_y = p_corr_mod_y + m_corr_y-event.y
    m_corr_x = event.x
    m_corr_y = event.y

def zoom(event):
    global scale
    global p_corr_mod_x
    global p_corr_mod_y
    if event.delta > 0:
        frame.scale(ALL,p_corr_mod_x + event.x, p_corr_mod_y + event.y, 1.25, 1.25)
        scale = scale * 1.25
    elif event.delta <0:
        frame.scale(ALL,p_corr_mod_x + event.x, p_corr_mod_y + event.y, .8, .8)
        scale = scale * .8
        
def dragtool(event):
    global t_corr_x
    global t_corr_y
    global p_corr_mod_x
    global p_corr_mod_y
    global dragtoolvar
    new_x = event.x + p_corr_mod_x
    new_y = event.y + p_corr_mod_y
    global frame
    if current_tool == 2:
        frame.coords(dragtoolvar,(t_corr_x,t_corr_y,new_x,new_x-t_corr_x+t_corr_y))
    elif ((current_tool == 4)|(current_tool == 3)|(current_tool == 101)):
        frame.coords(dragtoolvar,(t_corr_x,t_corr_y,new_x,new_y))
    elif current_tool == 1:
        radius = math.sqrt((t_corr_x - new_x)*(t_corr_x - new_x) + (t_corr_y - new_y)*(t_corr_y - new_y))
        frame.coords(dragtoolvar,t_corr_x-radius,t_corr_y-radius,t_corr_x+radius,t_corr_y+radius)

def ctrl_dn(event):
    global ctrl_var
    ctrl_var = 1

def ctrl_up(event):
    global ctrl_var
    ctrl_var = 0
        

    
###################################################################################
###################################################################################









    

############                  Toobar Function Setters:                #############
###################################################################################

def cleartoolbars():
    global button1
    global button2
    global button3
    global button4
    global button5
    global button6
    global button7
    global abutton1
    global abutton2
    button1.config(relief=RAISED)
    button2.config(relief=RAISED)
    button3.config(relief=RAISED)
    button4.config(relief=RAISED)
    button5.config(relief=RAISED)
    button6.config(relief=RAISED)
    button7.config(relief=RAISED)
    abutton1.config(relief=RAISED)
    abutton2.config(relief=RAISED)
    

def circle():
    global current_tool
    global button4
    cleartoolbars()
    button4.config(relief=SUNKEN)
    current_tool = 1

def square():
    global current_tool
    global button2
    cleartoolbars()
    button2.config(relief=SUNKEN)
    current_tool = 2

def line():
    global current_tool
    global button1
    cleartoolbars()
    button1.config(relief=SUNKEN)
    current_tool = 3

def rectangle():
    global current_tool
    global button3
    cleartoolbars()
    button3.config(relief=SUNKEN)
    current_tool = 4

def arc():
    global current_tool
    global button5
    cleartoolbars()
    button5.config(relief=SUNKEN)
    current_tool = 5

def polygon():
    global current_tool
    global button6
    cleartoolbars()
    button6.config(relief=SUNKEN)
    current_tool = 6

def text():
    global current_tool
    global button7
    cleartoolbars()
    button7.config(relief=SUNKEN)
    current_tool = 7

def arrow():
    global current_tool
    global abutton1
    cleartoolbars()
    abutton1.config(relief=SUNKEN)
    current_tool = 100

def rectangleselect():
    global current_tool
    global abutton2
    cleartoolbars()
    abutton2.config(relief=SUNKEN)
    current_tool = 101
    
###################################################################################
###################################################################################












    
############          Drawing Functions: Basic Shapes/Lines           #############
###################################################################################

def draw_circle(x,y,rad):
    global file_data
    global scale
    dummy = 0
    temp = [1,x/scale,y/scale,rad/scale/scale,dummy]
    render(temp)
    file_data.append(temp)
    
def draw_square(x,y,x2,y2):
    global file_data
    global scale
    dummy = 0
    temp = [2,x,y,x2,y2,dummy]
    render(temp)
    file_data.append(temp)

def draw_rectangle(x,y,x2,y2):
    global file_data
    global scale
    dummy = 0
    temp = [3,x,y,x2,y2,dummy]
    render(temp)
    file_data.append(temp)
    
def draw_line(x0,y0,x1,y1):
    global file_data
    global scale
    dummy = 0
    temp = [4,x0,y0,x1,y1,dummy]
    render(temp)
    file_data.append(temp)

def draw_dot(x,y):
    global frame
    global scale
    frame.create_rectangle(x-1,y-1,x+1,y+1,outline = 'blue', fill = 'blue')
###################################################################################
###################################################################################











############            Drawing Function Launcher, "Render"  :        #############
###################################################################################
def render(temp):
    #Adds an object to the canvas, and saves the object ID to the global data object
    #(temp references global temp object, and by updating it here, changes are
    #seen globally)    
    global frame
    global scale
    #find out what shape is to be rendered:
    if temp[0] == 1:
        #It's a circle
        temp[4] = frame.create_oval((temp[1]-temp[3])*scale,(temp[2]-temp[3])*scale,(temp[1]+temp[3])*scale,(temp[2]+temp[3])*scale,fill="", outline = "black")
    elif temp[0] == 2:
        #It's a square
        temp[5] = frame.create_rectangle(temp[1]*scale,temp[2]*scale,temp[3]*scale,(temp[3]-temp[1]+temp[2])*scale, outline= "black", fill = "")
    elif temp[0] == 3:
        #It's a rectangle
        temp[5] = frame.create_rectangle(temp[1]*scale,temp[2]*scale,temp[3]*scale,temp[4]*scale,outline="black", fill="")
    elif temp[0] == 4:
        temp[5] = frame.create_line(temp[1]*scale,temp[2]*scale,temp[3]*scale,temp[4]*scale,fill="black")

def pre_render():
    #Takes a new file, and re-creates the ID's for graphic objects. This may also be called to
    #"garbage collect" after many shapes have been created, however, it may be time consuming for
    #complex PICTOSS files (many objects)
    global file_data
    for temp in file_data:
        render(temp)
    
def test_func():
    global sellist
    global frame
    for obj in sellist:
        frame.itemconfigure(obj,fill='yellow', outline='orange', width=10)
    

def draw_shape(x0,y0,x1,y1):
    #detect what the current shape is.
    global scale
    global current_tool
    box.showwarning("Coordinates",(x0,y0,x1,y1))
    #first check to see if a selection tool is active
    if current_tool == 100:
        arrow_select(x1,y1)
    elif current_tool == 101:
        rectangle_select(x0,y0,x1,y1)
    elif current_tool == 1:
        radius = math.sqrt((x0 - x1)*(x0 - x1) + (y0 - y1)*(y0 - y1))
        draw_circle(x0, y0, radius)
    elif current_tool == 2:
        draw_square(x0,y0,x1,y1)
    elif current_tool == 3:
        draw_line(x0,y0,x1,y1)
    elif current_tool == 4:
        draw_rectangle(x0,y0,x1,y1)
##################################################################################
##################################################################################
      

############            Selection Functions, grouping, etc            #############
###################################################################################

def arrow_select(x,y):
    box.showwarning("Zzzzzz...", "......... (To be implemented) - Arrow Select")
def rectangle_select(x0,y0,x1,y1):
    global frame
    global sellist
    global ctrl_var
    #check to see if control is held down...if it is, don't clear the currently selected objects...just add to it
    if ctrl_var == 1:
        sellist=list(sellist)+ list(frame.find_enclosed(x0,y0,x1,y1))
        #remove any duplicate objects
        sellist=list(set(sellist))
    else:
        sellist = frame.find_enclosed(x0,y0,x1,y1)

def grid_toggle():
    global grid
    global frame
    if grid != []:
        for line in grid:
            frame.delete(line)
        grid = []
    else:
        #loop here to draw a massive grid, 100x100 inches
        for x in range (-50,50):
            for y in range(-50,50):
                line = frame.create_line(-50,y,50,y)
                grid.append(line)
                line = frame.create_line(x,-50,x,50)
                grid.append(line)
        
        
##################################################################################
##################################################################################

# create a GUI!
root = Tk()
menu = Menu(root)
root.config(menu=menu)
grid = []

file_data = []
t_corr_x = 0
t_corr_y = 0
m_corr_x = 0
m_corr_y = 0
p_corr_mod_x = 0
p_corr_mod_y = 0
current_tool = 0
dragtoolvar = 0
ctrl_var = 0
#keep track of current zoom...
scale = 1

#stores currently selected objects
sellist = []

filemenu = Menu(menu)
menu.add_cascade(label="File", menu=filemenu)
filemenu.add_command(label="New", command=New)
filemenu.add_command(label="Open", command=Open)
filemenu.add_command(label="Save", command=Save)
filemenu.add_command(label="Export", command=Export)
filemenu.add_command(label="Generate G-Code", command=generate_g_code)
filemenu.add_command(label="1 inch grid toggle", command=grid_toggle)
filemenu.add_separator()
filemenu.add_command(label="Exit", command=Exit)

helpmenu = Menu(menu)
menu.add_cascade(label="Help", menu=helpmenu)
helpmenu.add_command(label="Contents", command=Contents)
helpmenu.add_command(label="About", command=About)

toolbar = Frame(root, bd=1, relief=RAISED)
img = PhotoImage(file = "Resources\Toolbar_Icons\_line.gif")
button1 = Button(toolbar,image = img, relief=RAISED,command=line)
button1.pack(side=LEFT, padx=2, pady=2)
img2 = PhotoImage(file = "Resources\Toolbar_Icons\_square.gif")
button2 = Button(toolbar,image = img2, relief=RAISED,command=square)
button2.pack(side=LEFT, padx=2, pady=2)
img3 = PhotoImage(file = "Resources\Toolbar_Icons\_rectangle.gif")
button3 = Button(toolbar,image = img3, relief=RAISED,command=rectangle)
button3.pack(side=LEFT, padx=2, pady=2)

img4 = PhotoImage(file = "Resources\Toolbar_Icons\_circle.gif")
button4 = Button(toolbar,image = img4, relief=RAISED,command=circle)
button4.pack(side=LEFT, padx=2, pady=2)
img5 = PhotoImage(file = "Resources\Toolbar_Icons\_arc.gif")
button5 = Button(toolbar,image = img5, relief=RAISED,command=arc)
button5.pack(side=LEFT, padx=2, pady=2)
img6 = PhotoImage(file = "Resources\Toolbar_Icons\_polygon.gif")
button6 = Button(toolbar,image = img6, relief=RAISED,command=polygon)
button6.pack(side=LEFT, padx=2, pady=2)
img7 = PhotoImage(file = "Resources\Toolbar_Icons\_text.gif")
button7 = Button(toolbar,image = img7, relief=RAISED,command=text)
button7.pack(side=LEFT, padx=2, pady=2)
toolbar.pack(side=TOP, fill=X)

toolbar2 = Frame(root, bd=2, relief=RAISED)
img_2_1 = PhotoImage(file = "Resources\Toolbar_Icons\_selectarrow.gif")
abutton1 = Button(toolbar2,image = img_2_1, relief=RAISED,command=arrow)
abutton1.pack(side=LEFT, padx=2, pady=2)
img_2_2 = PhotoImage(file = "Resources\Toolbar_Icons\_selectbox.gif")
abutton2 = Button(toolbar2,image = img_2_2, relief=RAISED,command=rectangleselect)
abutton2.pack(side=LEFT, padx=2, pady=2)
img_2_3 = PhotoImage(file = "Resources\Toolbar_Icons\_test.gif")
abutton3 = Button(toolbar2,image = img_2_3, relief=RAISED,command=test_func)
abutton3.pack(side=LEFT, padx=2, pady=2)
toolbar2.pack(side=TOP, fill=X)

frame = Canvas(xscrollincrement=1,yscrollincrement=1)

frame.bind("<Button 3>",grab)
frame.bind("<B3-Motion>",drag)
root.bind("<KeyRelease-Control_L>",ctrl_up)
root.bind("<KeyPress-Control_L>",ctrl_dn)
root.bind("<MouseWheel>",zoom)
frame.config(scrollregion=frame.bbox(ALL))


frame.bind("<Button-1>", left_click_down)
frame.bind("<B1-Motion>",dragtool)
frame.bind("<ButtonRelease-1>", left_click_up)
frame.pack(fill=BOTH, expand=YES)

mainloop()
